#include "Analyzer.h"
#include "Misc.h"
#include <sstream>


//Default CTOR
Analyzer::Analyzer(){
	this->vars = new map<string,int>;
	this->vars_and_types = new map<string,string>;
	this->funcs = new map<string,int>;	
}

//Destructor
Analyzer::~Analyzer(){
	delete(vars);
	vars=NULL;
	delete(vars_and_types);
	vars_and_types=NULL;
	delete(funcs);
	funcs = NULL;	
}
//Copy CTOR
Analyzer::Analyzer(const Analyzer& a) { 
	this->vars = new map<string,int>;
	map<string,int>::iterator it_v;
	for(it_v = a.vars->begin();it_v!=a.vars->end();it_v++)
		this->vars->insert(pair<string,int>((*it_v).first,(*it_v).second));

	this->vars_and_types = new map<string,string>;
	map<string,string>::iterator it_vt;
	for(it_vt = a.vars_and_types->begin();it_vt!=a.vars_and_types->end();it_vt++)
		this->vars_and_types->insert(pair<string,string>((*it_vt).first,(*it_vt).second));

	this->funcs = new map<string,int>;
	map<string,int>::iterator it;
	for(it = a.funcs->begin();it!=a.funcs->end();it++)
		this->funcs->insert(pair<string,int>((*it).first,(*it).second));
}

//Assignment overloading
const Analyzer& Analyzer::operator=(const Analyzer& a){
	if (this!=&a) { 
		this->assign(a);
	}
		return *this;
}

const Analyzer& Analyzer::assign(const Analyzer& a){
	
	map<string,int>* new_vars = new map<string,int>;
	map<string,int>::iterator it_v;
	for(it_v = a.vars->begin();it_v!=a.vars->end();it_v++)
		new_vars->insert(pair<string,int>((*it_v).first,(*it_v).second));

	map<string,string>* new_vars_and_types = new map<string,string>;
	map<string,string>::iterator it_vt;
	for(it_vt = a.vars_and_types->begin();it_vt!=a.vars_and_types->end();it_vt++)
		new_vars_and_types->insert(pair<string,string>((*it_vt).first,(*it_vt).second));

	map<string,int>* new_funcs = new map<string,int>;
	map<string,int>::iterator it;
	for(it = a.funcs->begin();it!=a.funcs->end();it++)
		new_funcs->insert(pair<string,int>((*it).first,(*it).second));

	delete(this->vars);
	delete(this->vars_and_types);
	delete(this->funcs);

	this->vars = new_vars;
	this->vars_and_types = new_vars_and_types;
	this->funcs = new_funcs;
	return *this;
}






vector<string> Analyzer::getErrors()const{
	return this->errorVector;
}

map<string,int>* Analyzer::getFunctions(){
	return this->funcs;
}
map<string,int>* Analyzer::getVariables(){
	return this->vars;
}

string Analyzer::intToString(int i){
    std::string s;
    std::stringstream out;
	out<<i;
	s = out.str();
	return s;
}




 void Analyzer::checkBrackets(map<int,vector<Token>>* m)
 {
	 int count_bracket = 0; // {}
	 int count_parentheses = 0; // ()
	 int count_square_bracket = 0; // []
	 map<int,vector<Token>>::iterator row_iter;
	 vector<Token>::iterator token_iter;
	 unsigned int line_num = 0;
	 for(row_iter = m->begin() ; row_iter!=m->end() ; row_iter++){
		 line_num++;
		 for (token_iter=(*row_iter).second.begin() ; token_iter!=(*row_iter).second.end() ; token_iter++)
		 {
			 if((*token_iter).getType() == "delimiters")
			 {
				 if ((*token_iter).getValue().compare("[")==0)
					 count_square_bracket++;
				 if ((*token_iter).getValue().compare("]")==0)
					 count_square_bracket--;
				 if ((*token_iter).getValue().compare("(")==0)
					 count_parentheses++;
				 if ((*token_iter).getValue().compare(")")==0)
					 count_parentheses--;
				 if ((*token_iter).getValue().compare("{")==0)
					 count_bracket++;
				 if ((*token_iter).getValue().compare("}")==0)
					 count_bracket--;
				 //checks...
				 if(count_bracket<0)
				 {
					string e = "Line: " + intToString((*token_iter).getLineId()) +
						" Error - Number of open \"{\" does not match number of close \"}\" close before open";
					this->errorVector.push_back(e);
					count_bracket = 0;
				 }
				 if(count_parentheses<0)
				 {
					string e = "Line: " + intToString((*token_iter).getLineId());
					e+=	" Error - Number of open \"(\" does not match number of close \")\" close before open";
					this->errorVector.push_back(e);
					count_parentheses = 0;
				 }
				 if(count_square_bracket<0)
				 {
					string e = "Line: " + intToString((*token_iter).getLineId()) +
						" Error - Number of open \"[\" does not match number of close \"]\" close before open";
					this->errorVector.push_back(e);
					count_square_bracket = 0;
				 }
			 }
			 else 
				continue; //it's not a delmiter no need to check.
		 }
	 }
	 if(count_bracket>0)
	 {
		string e = "Line: " + intToString(line_num) +
		" Error - Number of open \"{\" does not match number of close \"}\" There are ";
		e+= intToString(count_bracket) + " open more than close";
		this->errorVector.push_back(e);
	 }
	 if(count_parentheses>0)
	 {
		string e = "Line: " + intToString(line_num) +
		" Error - Number of open \"(\" does not match number of close \")\" There are ";
		e+= intToString(count_parentheses) + " open more than close";
		this->errorVector.push_back(e);
	 }
	 if(count_square_bracket>0)
	 {
		string e = "Line: " + intToString(line_num) +
		" Error - Number of open \"[\" does not match number of close \"]\" There are ";
		e+= intToString(count_square_bracket) + " open more than close";
		this->errorVector.push_back(e);
	 }
 }

 void Analyzer::checkIdentifierLegality(map<int,vector<Token>>* m)
 {
	int line_id;
	map<int,vector<Token>>::iterator row_iter;
	vector<Token>::iterator token_iter;
	map<string,int>::iterator iter_var;
	map<string,int>::iterator iter_func;
	Token t_last;
	Token t_last_sec;
	Token tmp_token;
	int token_counter = 0;
	for(row_iter = m->begin() ; row_iter!=m->end() ; row_iter++)
	{
		for (token_iter=(*row_iter).second.begin() ; token_iter!=(*row_iter).second.end() ; token_iter++)
		{
			if((*row_iter).second.size() == 0) // no tokens at this line, an empty line
				continue;

			if(token_counter == 0)
			{
				t_last = Token();
			}
			if(token_counter == 1)
			{		
				t_last = tmp_token;
				t_last_sec = Token();
			}
			else
			{
				t_last_sec = t_last;
				t_last = tmp_token;
			}
			tmp_token = *token_iter;
			token_counter++;

			 line_id = (*token_iter).getLineId();
			 
			 if ((*token_iter).getType().compare("Identifiers")==0)
			 {
				 //checks MAIN
				 if((*token_iter).getValue().compare("MAIN")==0 && token_counter==1) 
					 continue; //this is the only Idetifier which don't need predefiend type before it
				 else if((*token_iter).getValue().compare("MAIN") != 0 && token_counter == 1)
				 {
					string e = "MAIN Error - The first Token of file should be MAIN, instead it's " + (*token_iter).getValue();
					this->errorVector.push_back(e);
				 }

				 //it's NOT legal identifier, i.e 12len name of Identifiers has to start from alphabetic character
				 if(!isLegaldentifier((*token_iter).getValue())) 
				 {
					string e = "Line: " + intToString(line_id) + " Error - Identifier: \"";
					e+= (*token_iter).getValue() + "\" is illegal, Identifiers NOT start from alphabetic character";
					this->errorVector.push_back(e);
				 }
				 iter_var = this->vars->find((*token_iter).getValue());
				 iter_func = this->funcs->find((*token_iter).getValue());
				 if(iter_var == this->vars->end() && iter_func == this->funcs->end()) //didn't found this Identifier in vars and funcs
				 {
					 //check there is Predefined type before it
					if(t_last.getType().compare("predefined") == 0 && t_last_sec.getType().compare("predefined") == 0)
					{
						string e = "Line: " + intToString(line_id) + " Error - Identifier: \"";
						e+= (*token_iter).getValue() + "\" is illegal, identifier has two Predefined types before it";
						this->errorVector.push_back(e);	
					}
					else if(t_last.getType().compare("predefined") != 0)
					{
						string e = "Line: " + intToString(line_id) + " Error - Identifier: \"";
						e+= (*token_iter).getValue() + "\" is illegal, identifier has to be declared before using it";
						this->errorVector.push_back(e);	
					}
					else //it's a legal Identifier
					{
						//need to choose if it's func or var
						if(t_last.getValue().compare("function") == 0)
							this->funcs->insert(pair<string,int>((*token_iter).getValue(),line_id)); //adds it to func database
						else
						{
							this->vars->insert(pair<string,int>((*token_iter).getValue(),line_id)); //adds it to var database.
							this->vars_and_types->insert(pair<string,string>((*token_iter).getValue(), t_last.getValue())); //adds it to var_and_type database
						}

					}
				}
				else // this Identifier has been declared
				{
					if(t_last.getType().compare("predefined") == 0)
					{
						if(t_last.getValue().compare("function") == 0)
						{
							string e = "Line: " + intToString(line_id) + " Error - function: \"";
							e+=(*token_iter).getValue() + "\" already declared";
							this->errorVector.push_back(e);
						}
						else //it's a var
						{
							string e = "Line: " + intToString(line_id) + " Error - Identifier: \"";
							e+=(*token_iter).getValue() + "\" already declared";
							this->errorVector.push_back(e);
						}
					}
				}
				
			}
		} //end token_iter for
	} // end row_iter for
}

void Analyzer::checkMainLegality(map<int,vector<Token>>* m){
	vector<Token> first_row = m->begin()->second;
	if (first_row.empty() || first_row.size()<3)
		this->errorVector.push_back("Error: Prgoram does not with Main()\n");
	else {
		if ((first_row[0].getValue().compare("Main")!=0) || first_row[1].getValue().compare("(")!=0 || first_row[2].getValue().compare(")")!=0)
			this->errorVector.push_back("Error: Prgoram does not start with Main()\n");

	}
}

map<string,string>* Analyzer::getTypesOfVars(){
	return this->vars_and_types;
}

 bool Analyzer::analyze(map<int,vector<Token>>* m) { 
	this->checkMainLegality(m);
	int multipleTypes = this->checkMultipleTypes(m);

			
	bool ifelse = checkIfElse(m);
	//If there is error, insert to error vector
	if(!ifelse) {
			this->errorVector.push_back("Error: Number of \"else\" is greater than number of \"if\" in the file ");
			string e = "Keyword \"else\" in line "+intToString(this->getLastElseLine(m))+" has no matching \"if\""+"\n";
			this->errorVector.push_back(e);
	}
	
	this->checkIdentifierLegality(m);
	this->checkBrackets(m);

	return true;
 }


 
 //Return true if the code by determine if there are multiple predefined types in a row
 //Otherwise inserts the correct error to the error vector and returns false 

 int Analyzer::checkMultipleTypes(map<int,vector<Token>>* m)  {
	Token Last_token;
	Token second_last_token;
	vector<Token> previous_row;
	vector<Token> row;
	
	bool succ = true;
	bool wroteMultiple = false;
	bool wrotePreAndKey = false;
	
	for(unsigned int i=1;i<=m->size();i++){

		//Adjust the lines
		if (i>1)
			previous_row = (*m)[i-1];
		row = (*m)[i];

		Last_token = this->getLastTokenFromRow(&previous_row);
		
		if (row.size()==0)
			continue;
		//Predefined and a Keyword after
		if (row[0].getType().compare("keywords")==0 && Last_token.getType().compare("predefined")==0) {
			if (!wrotePreAndKey) {
				this->errorVector.push_back("Error: keyword after a predefined token found ");
				wrotePreAndKey = true;
			}
			succ = false;
			this->errorVector.push_back("Error in line "+intToString((int)i)+" : keyword "+"\""+row[0].getValue()+"\""+" in line "+intToString((int)i)+", after predefined token "+"\""+Last_token.getValue()+"\""+" in line "+intToString(i-1)+"\n");
		}
		//Predefined and Predefined again after
		if (row[0].getType().compare("predefined")==0 && Last_token.getType().compare("predefined")==0) {
			if (!wroteMultiple) {
				this->errorVector.push_back("Error: multiple adjacent predefined types found");
				wroteMultiple = true;
			}
			succ = false;
			this->errorVector.push_back("Error in line "+intToString((int)i)+" : multiple adjacent predefined types: "+"\""+row[0].getValue()+"\""+" in line "+intToString((int)i)+", and "+"\""+Last_token.getValue()+"\""+" in line "+intToString(i-1)+"\n");
		}
					
		
		if (row.size()>=2) {
			for(unsigned int k=0;k<=row.size()-2 ;k++) {
				if (row[k].getType().compare("predefined")==0 && row[k+1].getType().compare("predefined")==0){
					if (!wroteMultiple) {
						this->errorVector.push_back("Error: multiple adjacent predefined types found");
						wroteMultiple = true;
					}
					succ = false;
					this->errorVector.push_back("Error in line "+intToString((int)i)+" : multiple adjacent predefined types: "+"\""+row[k].getValue()+"\""+", "+"\""+row[k+1].getValue()+"\""+"\n");
				}
				//Predefined and a Keyword after
				if (row[k].getType().compare("predefined")==0 && row[k+1].getType().compare("keywords")==0) {
					if (!wrotePreAndKey) {
						this->errorVector.push_back("Error: keyword after a predefined token found ");
						wrotePreAndKey = true;
					}
					succ = false;
					this->errorVector.push_back("Error in line "+intToString((int)i)+" : keyword "+"\""+row[0].getValue()+"\""+" in line "+intToString((int)i)+", after predefined token "+"\""+Last_token.getValue()+"\""+" in line "+intToString(i-1)+"\n");
				}
			}
		}

	}
	return succ;
 }

  Token Analyzer::getLastTokenFromRow(vector<Token>* row){
	  if (row->size()==0)
		  return Token(-1,"","no last");
	  else 
		  return (row->back());
 }

 Token Analyzer::getSecondLastTokenFromRow(vector<Token>* row){
	 vector<Token>::iterator iter;
	 iter= row->begin();
	 if (row->size()<2) 
		 return Token(-10,"","no second");
	 else {
		 for (unsigned int i=2;i<row->size();i++)
			 iter++;
		 return (*iter);
	 }
 }

	
 const bool Analyzer::checkIfElse(map<int,vector<Token>>* m)  const{ 
	 int countIfs = 0;
	 int countElse = 0;
	 map<int,vector<Token>>::iterator row_iter;
	 vector<Token>::iterator token_iter;
	 for(row_iter = m->begin();row_iter!=m->end();row_iter++){
		 for (token_iter=(*row_iter).second.begin();token_iter!=(*row_iter).second.end();token_iter++){
			 if ((*token_iter).getValue().compare("if")==0)
				 countIfs++;
			 if ((*token_iter).getValue().compare("else")==0)
				 countElse++;	
		 }
	 }
	 return countIfs>=countElse;
 }



 int Analyzer::getLastElseLine(map<int,vector<Token>>* m) const{
	 int lineId=0;

	 map<int,vector<Token>>::iterator row_iter;
	 vector<Token>::iterator token_iter;
	 for(row_iter = m->begin();row_iter!=m->end();row_iter++){
		 for (token_iter=(*row_iter).second.begin();token_iter!=(*row_iter).second.end();token_iter++){
			 if ((*token_iter).getValue().compare("else")==0)
				 lineId = row_iter->first;
		 }
	 }
	


	 return lineId;
 }


 const bool Analyzer::isLegaldentifier(string& id) { 
	 char firstChar = id[0];
	 if (firstChar>='a'&&firstChar<='z'&&firstChar>='A'&&firstChar>='Z')
		 return true;
	 return false;
 }






